﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MvcEngine.Core;
using System.Web.Routing;
using MvcEngine.Mvc.Routing;
using System.Web.Mvc;
using MvcEngine.Core.Localization;
using System.Web;
using MvcEngine.Mvc.View.ViewEngine;
using MvcEngine.Mvc.Infrastructure;
using MvcEngine.Mvc.View.Skin;
using MvcEngine.Mvc.Filters;
using MvcEngine.Mvc.StarterSite.Controllers;
using MvcEngine.Core.Services;
using MvcEngine.Core.Website;
using MvcEngine.Mvc.ModelBinders;
using MvcEngine.Core.Content;
using MvcEngine.Mvc.BackOffice.Controllers;
using MvcEngine.Core.Registry;
using MvcEngine.Mvc.Models;
using MvcEngine.Core.Validation;
using MvcEngine.Mvc.Areas.BackOffice;
using MvcEngine.Mvc.Areas.StarterSite;
using MvcEngine.Mvc.Models.Attributes;
using Akronus.TaskScheduler;
using Akronus.TaskScheduler.LinqDataService;
using Akronus.TaskScheduler.LinqLogger;
using System.Threading;
using System.Reflection;
using AutoMapper;
using MvcEngine.Core.Account;

namespace MvcEngine.Mvc
{
    public class MvcEngineApplication : System.Web.HttpApplication
    {
        TaskService taskService = null;

        protected void Application_Start()
        {
            OnStart();
        }

        protected void Application_End()
        {
            HttpRuntime runtime =
    (HttpRuntime)typeof(System.Web.HttpRuntime).InvokeMember("_theRuntime",
                    BindingFlags.NonPublic
                    | BindingFlags.Static
                    | BindingFlags.GetField,
                    null,
                    null,
                    null);

            if (runtime == null)
            {
                MvcEngine.Core.Helpers.LogHelper.Current.WriteError("Runtime = null.");
                return;
            }

            string shutDownMessage =
                (string)runtime.GetType().InvokeMember("_shutDownMessage",
                                BindingFlags.NonPublic
                                | BindingFlags.Instance
                                | BindingFlags.GetField,
                                null,
                                runtime,
                                null);

            string shutDownStack =
                (string)runtime.GetType().InvokeMember("_shutDownStack",
                               BindingFlags.NonPublic
                               | BindingFlags.Instance
                               | BindingFlags.GetField,
                               null,
                               runtime,
                               null);

            MvcEngine.Core.Helpers.LogHelper.Current.WriteError(shutDownMessage + ": " + shutDownStack);

            taskService.StopTasks();
            taskService.RunTask("KeepAliveTask");
        }

        private void OnStart()
        {
            SetupAutoMapper();
            SetupContainer();
            RegisterRoutes();
            RegisterControlCommands();
            SetupFactory();
            RegisterEngines(ViewEngines.Engines);
            SetupActionFilters();
            RegisterValidators();
            RegisterModelBinders();

            taskService = new TaskService(new TaskInfoDataService(), new TaskLogDataService());
            taskService.StartTasks();
        }

        private void SetupAutoMapper()
        {
            //AutoMapperConfiguration.Configure();
            Mapper.CreateMap<Site, SiteItemViewModel>().ForMember(s => s.Skin, item => item.MapFrom(src => src.SkinId));
            Mapper.CreateMap<AccountViewModel, User>();

            //Mapper.AssertConfigurationIsValid();
            //Mapper.Initialize(x => x.AddProfile<ViewModelProfile>());


        }

        private void RegisterValidators()
        {
            ModelMetadataProviders.Current = new FieldTemplateMetadataProvider();
            DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredFieldValidatorAttribute), typeof(RequiredFieldValidatorAdapter));
            DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(LengthValidatorAttribute), typeof(LengthFieldValidatorAdapter));
        }

        private void RegisterControlCommands()
        {
            var modules = ContentManager.Current.GetModules();
            foreach (var item in modules)
            {
                var controls = ContentManager.Current.GetControls(item.Id);
                foreach (var control in controls)
                {
                    if (!string.IsNullOrEmpty(control.ControlServiceType))
                    {
                        Type type = Type.GetType(control.ControlServiceType);
                        IControlService service = (IControlService)IoC.Resolve(type);

                        var commands = service.GetCommands();

                        foreach (var command in commands)
                        {
                            command.ControlId = control.Id;
                            CommandRegistrator.Current.Add(command);
                        }
                    }
                }
            }
        }

        private void SetupActionFilters()
        {
            IActionFilterRegistry registry = IoC.Resolve<IActionFilterRegistry>();
            registry.Clear();

            //Antiforgery
            registry.Add(Enumerable.Empty<IFilterCriteria>(), typeof(AntiForgeryActionFilter));
            registry.Add(Enumerable.Empty<IFilterCriteria>(), typeof(LanguageActionFilter));
            //registry.Add(Enumerable.Empty<IFilterCriteria>(), typeof(PageListActionFilter));
            //registry.Add(Enumerable.Empty<IFilterCriteria>(), typeof(ErrorFilter));
            //registry.Add(Enumerable.Empty<IFilterCriteria>(), typeof(AntiForgeryAuthorizationFilter));

            //Home controller
            ControllerActionCriteria homeCriteria = new ControllerActionCriteria();
            homeCriteria.AddAction<MvcEngine.Mvc.StarterSite.Controllers.HomeController>(a => a.Index(Guid.Empty));
            registry.Add(new[] { homeCriteria }, typeof(PageActionFilter));
            registry.Add(new[] { homeCriteria }, new ValidateInputAttribute(false));


            //--------------------------------------------------------------------------------------
            //------------------------------Common filters------------------------------------------
            //--------------------------------------------------------------------------------------
            ControllerCriteria commonCriteria = new ControllerCriteria();
            //StarterSite
            commonCriteria.AddController(typeof(MvcEngine.Mvc.StarterSite.Controllers.HomeController));
            commonCriteria.AddController(typeof(MvcEngine.Mvc.StarterSite.Controllers.AccountController));
            //BackOffice
            //commonCriteria.AddController(typeof(MvcEngine.Mvc.BackOffice.Controllers.HomeController));
            commonCriteria.AddController(typeof(MvcEngine.Mvc.BackOffice.Controllers.UserController));
            commonCriteria.AddController(typeof(MvcEngine.Mvc.BackOffice.Controllers.ErrorController));
            commonCriteria.AddController(typeof(MvcEngine.Mvc.BackOffice.Controllers.LogController));
            commonCriteria.AddController(typeof(MvcEngine.Mvc.BackOffice.Controllers.ModuleController));
            commonCriteria.AddController(typeof(MvcEngine.Mvc.BackOffice.Controllers.PageContentController));
            commonCriteria.AddController(typeof(MvcEngine.Mvc.BackOffice.Controllers.PageController));
            commonCriteria.AddController(typeof(MvcEngine.Mvc.BackOffice.Controllers.SiteController));

            registry.Add(new[] { commonCriteria }, typeof(LanguageActionFilter));
            //--------------------------------------------------------------------------------------
            //------------------------------End of common filters-----------------------------------
            //--------------------------------------------------------------------------------------

            //--------------------------------------------------------------------------------------
            //---------------------------------Permissions------------------------------------------
            //--------------------------------------------------------------------------------------
            ControllerCriteria editCriteria = new ControllerCriteria();
            editCriteria.AddController(typeof(PageController));
            editCriteria.AddController(typeof(SiteController));
            editCriteria.AddController(typeof(MvcEngine.Mvc.BackOffice.Controllers.HomeController));
            editCriteria.AddController(typeof(PageContentController));
            editCriteria.AddController(typeof(LogController));
            editCriteria.AddController(typeof(ModuleController));
            registry.Add(new[] { editCriteria }, new RolePermissionAttribute(Constants.Roles.Administrators));

            ControllerActionCriteria editActionCriteria = new ControllerActionCriteria();
            editActionCriteria.AddAction<MvcEngine.Mvc.BackOffice.Controllers.UserController>(a => a.Delete(Guid.Empty));
            editActionCriteria.AddAction<MvcEngine.Mvc.BackOffice.Controllers.UserController>(a => a.Edit(Guid.Empty));
            editActionCriteria.AddAction<MvcEngine.Mvc.BackOffice.Controllers.UserController>(a => a.List(null));
            registry.Add(new[] { editActionCriteria }, new RolePermissionAttribute(Constants.Roles.Administrators));
            //--------------------------------------------------------------------------------------
            //---------------------------------End of permissions----------------------------------
            //--------------------------------------------------------------------------------------

            //Register modules action filters
            foreach (var item in IoC.ResolveAll<IControlService>())
            {
                item.RegisterActionFilters(registry);
            }
        }

        private void SetupFactory()
        {
            ControllerBuilder.Current.SetControllerFactory(new MvcEngineControllerFactory());
        }

        private void RegisterRoutes()
        {
            //RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);

            //AreaRegistration.RegisterAllAreas();
            AreaRegistrationContext commonContext = new AreaRegistrationContext("Common", RouteTable.Routes, null);
            CommonRoutes commonRoutes = new CommonRoutes();
            commonRoutes.RegisterArea(commonContext);

            AreaRegistrationContext backOfficeContext = new AreaRegistrationContext("BackOffice", RouteTable.Routes, null);
            BackOfficeRoutes backOfficeRoutes = new BackOfficeRoutes();
            backOfficeRoutes.RegisterArea(backOfficeContext);

            AreaRegistrationContext starterSiteContext = new AreaRegistrationContext("StarterSite", RouteTable.Routes, null);
            StarterSiteRoutes starterSiteRoutes = new StarterSiteRoutes();
            starterSiteRoutes.RegisterArea(starterSiteContext);
        }

        private void RegisterModelBinders()
        {
            ModelBinderDictionary binders = System.Web.Mvc.ModelBinders.Binders;
            binders[typeof(Site)] = new SiteModelBinder();
            binders[typeof(Page)] = new PageModelBinder();

            foreach (var item in IoC.ResolveAll<IControlService>())
            {
                item.RegisterModelBinders(binders);
            }
        }

        private void SetupContainer()
        {
            IoC.Initialize();
            IoC.RegisterInstance(RouteTable.Routes);
        }

        private void RegisterEngines(ViewEngineCollection engines)
        {
            engines.Clear();
            engines.Add(new CustomViewEngine(new DefaultSkinEngine()));
        }

        //protected void Application_BeginRequest(object sender, EventArgs e)
        //{
        //    if (SiteContext.Current.Site != null)
        //    {
        //        return;
        //    }
        //    throw new HttpException(404, ResourceReader.Current.GetString("ResourceNotFound", "The resource cannot be found."));
        //}

        protected void Application_EndRequest(object sender, EventArgs e)
        {
            foreach (var item in HttpContext.Current.Items)
            {
                IDisposable obj = item as IDisposable;
                if (obj != null)
                    obj.Dispose();
            }
        }
    }
}
